<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/limh/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/limh/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/limh/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/limh/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/limh/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/limh/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/limh/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Android Kotloin,">










<meta name="description" content="前言图片上传操作基本上是每个应用都会有的功能，但是由于Android碎片话严重，导致适配很繁琐，比如6.0以下版本、6.0的动态权限、7.0的FileProvider、8.0的特殊情况，再加上马上要出的Android Q权限变化。我个人项目中三方库能少用就少用，能自己解决，绝不用三方库，因为很多时候三方库不一定能满足自身要求，尤其是UI方面。下面记录下我项目中用到的图片操作，用Android原生方">
<meta name="keywords" content="Android Kotloin">
<meta property="og:type" content="article">
<meta property="og:title" content="Android原生拍照及相册选取图片">
<meta property="og:url" content="https://tdnr.gitee.io/limh/2019/04/20/android03/index.html">
<meta property="og:site_name" content="土逗牛肉">
<meta property="og:description" content="前言图片上传操作基本上是每个应用都会有的功能，但是由于Android碎片话严重，导致适配很繁琐，比如6.0以下版本、6.0的动态权限、7.0的FileProvider、8.0的特殊情况，再加上马上要出的Android Q权限变化。我个人项目中三方库能少用就少用，能自己解决，绝不用三方库，因为很多时候三方库不一定能满足自身要求，尤其是UI方面。下面记录下我项目中用到的图片操作，用Android原生方">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/10147880-50c35a7ae86029a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/10147880-922ac3f639a89ad0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:updated_time" content="2019-04-21T08:21:36.471Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android原生拍照及相册选取图片">
<meta name="twitter:description" content="前言图片上传操作基本上是每个应用都会有的功能，但是由于Android碎片话严重，导致适配很繁琐，比如6.0以下版本、6.0的动态权限、7.0的FileProvider、8.0的特殊情况，再加上马上要出的Android Q权限变化。我个人项目中三方库能少用就少用，能自己解决，绝不用三方库，因为很多时候三方库不一定能满足自身要求，尤其是UI方面。下面记录下我项目中用到的图片操作，用Android原生方">
<meta name="twitter:image" content="https://upload-images.jianshu.io/upload_images/10147880-50c35a7ae86029a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/limh/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://tdnr.gitee.io/limh/2019/04/20/android03/">





  <title>Android原生拍照及相册选取图片 | 土逗牛肉</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans ">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/limh/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">土逗牛肉</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/limh/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/limh/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/limh/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/limh/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/limh/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://tdnr.gitee.io/limh/limh/2019/04/20/android03/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="土逗牛肉">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/limh/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="土逗牛肉">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">Android原生拍照及相册选取图片</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-04-20T17:35:45+08:00">
                2019-04-20
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/limh/categories/Android/" itemprop="url" rel="index">
                    <span itemprop="name">Android</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>图片上传操作基本上是每个应用都会有的功能，但是由于Android碎片话严重，导致适配很繁琐，比如6.0以下版本、6.0的动态权限、7.0的FileProvider、8.0的特殊情况，再加上马上要出的Android Q权限变化。我个人项目中三方库能少用就少用，能自己解决，绝不用三方库，因为很多时候三方库不一定能满足自身要求，尤其是UI方面。下面记录下我项目中用到的图片操作，用Android原生方式实现，并且适配了Andrioid 7.0的FileProvider。</p>
<h2 id="动态权限"><a href="#动态权限" class="headerlink" title="动态权限"></a>动态权限</h2><p>6.0以上危险权限需要动态获取，那可能不知道哪些是危险权限，那么看下图：</p>
<p><img src="https://upload-images.jianshu.io/upload_images/10147880-50c35a7ae86029a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="危险权限列表"></p>
<p>选取图片用的文件读写和相机操作都需要动态获取，我们这里依然用原生实现，代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">override fun onResume() &#123;</span><br><span class="line">    super.onResume()</span><br><span class="line">    checkPermission()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * 获取权限</span><br><span class="line"> * 因为是一次请求完几个权限，不是在用到的时候再请求，</span><br><span class="line"> * 所以不需要重写onRequestPermissionsResult方法</span><br><span class="line"> */</span><br><span class="line">private fun checkPermission() &#123;</span><br><span class="line">    val storage = arrayOf(</span><br><span class="line">        Manifest.permission.WRITE_EXTERNAL_STORAGE,</span><br><span class="line">        Manifest.permission.READ_EXTERNAL_STORAGE</span><br><span class="line">    )</span><br><span class="line">    val camera = arrayOf(Manifest.permission.CAMERA)</span><br><span class="line">    //检查相机权限</span><br><span class="line">    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">        // 之前拒绝了权限，但没有点击 不再询问 这个时候让它继续请求权限</span><br><span class="line">        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) &#123;</span><br><span class="line">            Log.d(TAG, &quot;用户曾拒绝打开相机权限&quot;)</span><br><span class="line">            ActivityCompat.requestPermissions(this, camera, 100)</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            //注册相机权限</span><br><span class="line">            ActivityCompat.requestPermissions(this, camera, 100)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    //检查文件读写权限</span><br><span class="line">    if (ContextCompat.checkSelfPermission(</span><br><span class="line">            this,</span><br><span class="line">            Manifest.permission.READ_EXTERNAL_STORAGE</span><br><span class="line">        ) != PackageManager.PERMISSION_GRANTED</span><br><span class="line">        || ContextCompat.checkSelfPermission(</span><br><span class="line">            this,</span><br><span class="line">            Manifest.permission.WRITE_EXTERNAL_STORAGE</span><br><span class="line">        ) != PackageManager.PERMISSION_GRANTED</span><br><span class="line">    ) &#123;</span><br><span class="line">        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) &#123;</span><br><span class="line">            Log.d(TAG, &quot;用户曾拒绝打开文件读写权限&quot;)</span><br><span class="line">            ActivityCompat.requestPermissions(this, storage, 101)</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            //注册相机权限</span><br><span class="line">            ActivityCompat.requestPermissions(this, storage, 101)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="7-0FileProvider适配"><a href="#7-0FileProvider适配" class="headerlink" title="7.0FileProvider适配"></a>7.0FileProvider适配</h2><h4 id="第一步"><a href="#第一步" class="headerlink" title="第一步"></a>第一步</h4><p>在res目录下创建xml文件夹，在文件夹下创建一个文件，比如：file_paths.xml，文件里添加如下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span><br><span class="line">&lt;paths&gt;</span><br><span class="line">    &lt;files-path</span><br><span class="line">        name=&quot;files&quot;</span><br><span class="line">        path=&quot;/&quot;/&gt;</span><br><span class="line">    &lt;cache-path</span><br><span class="line">        name=&quot;cache&quot;</span><br><span class="line">        path=&quot;/&quot;/&gt;</span><br><span class="line">    &lt;external-path</span><br><span class="line">        name=&quot;external&quot;</span><br><span class="line">        path=&quot;/&quot;/&gt;</span><br><span class="line">    &lt;external-files-path</span><br><span class="line">        name=&quot;external_file_path&quot;</span><br><span class="line">        path=&quot;/&quot;/&gt;</span><br><span class="line">    &lt;external-cache-path</span><br><span class="line">        name=&quot;external_cache_path&quot;</span><br><span class="line">        path=&quot;/&quot;/&gt;</span><br><span class="line">&lt;/paths&gt;</span><br></pre></td></tr></table></figure>
<h4 id="第二步"><a href="#第二步" class="headerlink" title="第二步"></a>第二步</h4><p>在AndroidManifest.xml的application中添加provider标签，如下图所示：</p>
<p><img src="https://upload-images.jianshu.io/upload_images/10147880-922ac3f639a89ad0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="provider.png"></p>
<p>其中${applicationId}你可以直接这么写，也可以替换成你的包名。这两步配置好了后7.0适配算是完成了三分之二，后面如何在代码中读取文件，请耐心往后看。</p>
<h2 id="目录创建"><a href="#目录创建" class="headerlink" title="目录创建"></a>目录创建</h2><p>相机拍照后保存在手机DCIM/Image目录下，如下代码中getFileUri方法就是对7.0的兼容</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * @author limh</span><br><span class="line"> * @function</span><br><span class="line"> * @date 2019/4/19 9:40</span><br><span class="line"> */</span><br><span class="line">object FileUtils &#123;</span><br><span class="line">    val BASE_PATH = &quot;$&#123;Environment.getExternalStorageDirectory()&#125;/DCIM/Image&quot;</span><br><span class="line">    /**</span><br><span class="line">     * 根据文件名称 生成目录</span><br><span class="line">     * @param fileName 文件名称</span><br><span class="line">     */</span><br><span class="line">    fun getFilePath(fileName: String): String &#123;</span><br><span class="line">        val dir = File(BASE_PATH)</span><br><span class="line">        //如果目录不存在 先创建目录</span><br><span class="line">        if (!dir.exists()) &#123;</span><br><span class="line">            dir.mkdir()</span><br><span class="line">        &#125;</span><br><span class="line">        return &quot;$BASE_PATH/$fileName&quot;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取文件Uri</span><br><span class="line">     * @param fileName 文件名称</span><br><span class="line">     */</span><br><span class="line">    fun getFileUri(context: Context, fileName: String): Uri &#123;</span><br><span class="line">        val filePah = getFilePath(fileName)</span><br><span class="line">        return if (Build.VERSION.SDK_INT &gt;= 24) &#123;</span><br><span class="line">            FileProvider.getUriForFile(context, &quot;$&#123;BuildConfig.APPLICATION_ID&#125;.fileprovider&quot;, File(filePah))</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            Uri.fromFile(File(filePah))</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="打开相机"><a href="#打开相机" class="headerlink" title="打开相机"></a>打开相机</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 打开相机</span><br><span class="line"> */</span><br><span class="line">private fun openCamera() &#123;</span><br><span class="line">    Log.d(TAG, &quot;打开相机&quot;)</span><br><span class="line">    imgUri = FileUtils.getFileUri(context, &quot;&#123;$&#123;System.currentTimeMillis()&#125;&#125;.jpg&quot;)</span><br><span class="line">    // 创建Intent，用于启动手机的照相机拍照</span><br><span class="line">    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)</span><br><span class="line">    // 指定输出到文件uri中</span><br><span class="line">    intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)</span><br><span class="line">    // 启动intent开始拍照</span><br><span class="line">    startActivityForResult(intent, REQUEST_CAMERA)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>注意：上面打开相机方式拍照后文件会保存在imgUri中，onActivityResult回调不会返回数据，当回调成功后直接拿imgUri就是你拍的照片内容。拍照完成打开<strong>DCIM/Image/Album_xxxxxx.jpg</strong>，就是你刚拍摄的图片。</li>
</ul>
<h2 id="打开相册"><a href="#打开相册" class="headerlink" title="打开相册"></a>打开相册</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 打开相册</span><br><span class="line"> */</span><br><span class="line">private fun openAlbum() &#123;</span><br><span class="line">    Log.d(TAG, &quot;打开相册&quot;)</span><br><span class="line">    val albumIntent = Intent(Intent.ACTION_PICK)</span><br><span class="line">    albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, &quot;image/*&quot;)</span><br><span class="line">    startActivityForResult(albumIntent, REQUEST_ALBUM)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>注意：这种方式打开相册 打开相册后选择的图片uri会在intent的data中，需要我们从里面取.</li>
</ul>
<h2 id="原生裁剪"><a href="#原生裁剪" class="headerlink" title="原生裁剪"></a>原生裁剪</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 裁剪图片</span><br><span class="line"> * @param uri:要裁剪的文件</span><br><span class="line"> */</span><br><span class="line">private fun corpPic(uri: Uri?) &#123;</span><br><span class="line">    //裁剪后的文件名称</span><br><span class="line">    val fileName = &quot;Crop_$&#123;System.currentTimeMillis()&#125;.jpg&quot;</span><br><span class="line">    //裁剪后文件Uri</span><br><span class="line">    imgUri = FileUtils.getFileUri(context, fileName)</span><br><span class="line">    val intent = Intent(&quot;com.android.camera.action.CROP&quot;)</span><br><span class="line">    intent.setDataAndType(uri, &quot;image/*&quot;)</span><br><span class="line">    //以下两行添加，解决无法加载此图片的提示</span><br><span class="line">    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)</span><br><span class="line">    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)</span><br><span class="line">    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(File(FileUtils.getFilePath(fileName))))</span><br><span class="line">    intent.putExtra(&quot;crop&quot;, &quot;true&quot;)</span><br><span class="line">    intent.putExtra(&quot;aspectX&quot;, 1) // 裁剪框比例</span><br><span class="line">    intent.putExtra(&quot;aspectY&quot;, 1)</span><br><span class="line">    intent.putExtra(&quot;outputX&quot;, 100) // 输出图片大小</span><br><span class="line">    intent.putExtra(&quot;outputY&quot;, 100)</span><br><span class="line">    intent.putExtra(&quot;scale&quot;, false)</span><br><span class="line">    intent.putExtra(&quot;outputFormat&quot;, Bitmap.CompressFormat.JPEG.toString())</span><br><span class="line">    startActivityForResult(intent, REQUEST_CORP)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>-注意：裁剪输出目录一定要用Uri.fromFile生成，直接用imgUri的话会提示：无法保存经过裁剪的图片，图片也无法正常加载。</p>
<p>个人感觉原生裁剪很好用，可以设置裁剪比例和输出大小。设置完后图片很小，也不需要做压缩操作。</p>
<h2 id="相机相册以及裁剪回调"><a href="#相机相册以及裁剪回调" class="headerlink" title="相机相册以及裁剪回调"></a>相机相册以及裁剪回调</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) &#123;</span><br><span class="line">    super.onActivityResult(requestCode, resultCode, data)</span><br><span class="line">    Log.d(TAG, &quot;返回结果：$&#123;data ?: data.toString()&#125;&quot;)</span><br><span class="line">    if (requestCode == REQUEST_CAMERA) &#123;</span><br><span class="line">        //相机结果返回</span><br><span class="line">        if (resultCode == Activity.RESULT_OK) &#123;</span><br><span class="line">            imgUri?.let &#123; corpPic(it) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; else if (requestCode == REQUEST_ALBUM) &#123;</span><br><span class="line">        //相册返回结果</span><br><span class="line">        if (resultCode == Activity.RESULT_OK) &#123;</span><br><span class="line">            //选择的图片转存 在DCIM/Image目录</span><br><span class="line">            data?.let &#123; corpPic(it.data) &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; else if (requestCode == REQUEST_CORP) &#123;</span><br><span class="line">        //裁剪回调</span><br><span class="line">        Picasso.get().load(imgUri).memoryPolicy(MemoryPolicy.NO_CACHE).into(image)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>至此，整个相机相册以及裁剪操作完成，最终的结果都保存到Uri中，但是有时候还需要解析文件路径，这个解析就涉及到版本兼容，又是另一个大坑，5.0上下获取方式都不同，好歹踩过坑的人，因此再上一个文件路径解析工具，直接调用ImagePath.getRealPathFromUri将Uri转换为String路径：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * @author limh</span><br><span class="line"> * @function 解析图片路径</span><br><span class="line"> * @date 2019/2/25 11:29</span><br><span class="line"> */</span><br><span class="line">object ImagePath &#123;</span><br><span class="line">    /**</span><br><span class="line">     * 根据Uri获取图片的绝对路径</span><br><span class="line">     *</span><br><span class="line">     * @param context 上下文对象</span><br><span class="line">     * @param uri     图片的Uri</span><br><span class="line">     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null</span><br><span class="line">     */</span><br><span class="line">    fun getRealPathFromUri(context: Context, uri: Uri?): String? &#123;</span><br><span class="line">        if (null == uri) return &quot;&quot;</span><br><span class="line">        val sdkVersion = Build.VERSION.SDK_INT</span><br><span class="line">        return if (sdkVersion &gt;= 19) &#123;</span><br><span class="line">            getRealPathFromUriAboveApi19(context, uri)</span><br><span class="line">        &#125; else &#123; // api &lt; 19</span><br><span class="line">            getRealPathFromUriBelowAPI19(context, uri)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 适配api19以下(不包括api19),根据uri获取图片的绝对路径</span><br><span class="line">     *</span><br><span class="line">     * @param context 上下文对象</span><br><span class="line">     * @param uri     图片的Uri</span><br><span class="line">     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null</span><br><span class="line">     */</span><br><span class="line">    private fun getRealPathFromUriBelowAPI19(context: Context, uri: Uri): String? &#123;</span><br><span class="line">        return getDataColumn(context, uri, null, null)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 适配api19及以上,根据uri获取图片的绝对路径</span><br><span class="line">     *</span><br><span class="line">     * @param context 上下文对象</span><br><span class="line">     * @param uri     图片的Uri</span><br><span class="line">     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null</span><br><span class="line">     */</span><br><span class="line">    @SuppressLint(&quot;NewApi&quot;)</span><br><span class="line">    private fun getRealPathFromUriAboveApi19(context: Context, uri: Uri): String? &#123;</span><br><span class="line">        var filePath: String? = null</span><br><span class="line">        if (DocumentsContract.isDocumentUri(context, uri)) &#123;</span><br><span class="line">            // 如果是document类型的 uri, 则通过document id来进行处理</span><br><span class="line">            val documentId = DocumentsContract.getDocumentId(uri)</span><br><span class="line">            if (isMediaDocument(uri)) &#123; // MediaProvider</span><br><span class="line">                // 使用&apos;:&apos;分割</span><br><span class="line">                val id = documentId.split(&quot;:&quot;.toRegex()).dropLastWhile &#123; it.isEmpty() &#125;.toTypedArray()[1]</span><br><span class="line"></span><br><span class="line">                val selection = MediaStore.Images.Media._ID + &quot;=?&quot;</span><br><span class="line">                val selectionArgs = arrayOf(id)</span><br><span class="line">                filePath =</span><br><span class="line">                    getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs)</span><br><span class="line">            &#125; else if (isDownloadsDocument(uri)) &#123; // DownloadsProvider</span><br><span class="line">                val contentUri = ContentUris.withAppendedId(</span><br><span class="line">                    Uri.parse(&quot;content://downloads/public_downloads&quot;),</span><br><span class="line">                    java.lang.Long.valueOf(documentId)</span><br><span class="line">                )</span><br><span class="line">                filePath = getDataColumn(context, contentUri, null, null)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else if (&quot;content&quot;.equals(uri.scheme!!, ignoreCase = true)) &#123;</span><br><span class="line">            // 如果是 content 类型的 Uri</span><br><span class="line">            filePath = getDataColumn(context, uri, null, null)</span><br><span class="line">        &#125; else if (&quot;file&quot; == uri.scheme) &#123;</span><br><span class="line">            // 如果是 file 类型的 Uri,直接获取图片对应的路径</span><br><span class="line">            filePath = uri.path</span><br><span class="line">        &#125;</span><br><span class="line">        return filePath</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取数据库表中的 _data 列，即返回Uri对应的文件路径</span><br><span class="line">     *</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array&lt;String&gt;?): String? &#123;</span><br><span class="line">        var path: String? = null</span><br><span class="line"></span><br><span class="line">        val projection = arrayOf(MediaStore.Images.Media.DATA)</span><br><span class="line">        var cursor: Cursor? = null</span><br><span class="line">        try &#123;</span><br><span class="line">            cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)</span><br><span class="line">            if (cursor != null &amp;&amp; cursor.moveToFirst()) &#123;</span><br><span class="line">                val columnIndex = cursor.getColumnIndexOrThrow(projection[0])</span><br><span class="line">                path = cursor.getString(columnIndex)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (e: Exception) &#123;</span><br><span class="line">            cursor?.close()</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        return path</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * @param uri the Uri to check</span><br><span class="line">     * @return Whether the Uri authority is MediaProvider</span><br><span class="line">     */</span><br><span class="line">    private fun isMediaDocument(uri: Uri): Boolean &#123;</span><br><span class="line">        return &quot;com.android.providers.media.documents&quot; == uri.authority</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * @param uri the Uri to check</span><br><span class="line">     * @return Whether the Uri authority is DownloadsProvider</span><br><span class="line">     */</span><br><span class="line">    private fun isDownloadsDocument(uri: Uri): Boolean &#123;</span><br><span class="line">        return &quot;com.android.providers.downloads.documents&quot; == uri.authority</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>  生命不息，踩坑不止，很多Android三方库追根揭底也是对原生SDK的封装，多了解下原生的写法，你就是下一个轮子大神。</p>
<p>最后附上github地址：<br><a href="[https://github.com/limhGeek/Album](https://github.com/limhGeek/Album"><a href="https://github.com/limhGeek/Album" target="_blank" rel="noopener">https://github.com/limhGeek/Album</a>
</a><br>)</p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/limh/tags/Android-Kotloin/" rel="tag"># Android Kotloin</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/limh/2019/04/14/android01/" rel="next" title="抖音网红罗盘时钟">
                <i class="fa fa-chevron-left"></i> 抖音网红罗盘时钟
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/limh/2019/05/30/flutter4/" rel="prev" title="Flutter底部凸起导航栏">
                Flutter底部凸起导航栏 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
      <div id="SOHUCS"></div>
    </div>

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/limh/images/avatar.jpg" alt="土逗牛肉">
            
              <p class="site-author-name" itemprop="name">土逗牛肉</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/limh/archives/">
              
                  <span class="site-state-item-count">255</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/limh/categories/index.html">
                  <span class="site-state-item-count">8</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/limh/tags/index.html">
                  <span class="site-state-item-count">7</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/limhGeek" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="mailto:1591054297@qq.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.jianshu.com/u/f73b243e7cc9" target="_blank" title="简书">
                      
                        <i class="fa fa-fw fa-user-circle-o"></i>简书</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#动态权限"><span class="nav-number">2.</span> <span class="nav-text">动态权限</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-0FileProvider适配"><span class="nav-number">3.</span> <span class="nav-text">7.0FileProvider适配</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#第一步"><span class="nav-number">3.0.1.</span> <span class="nav-text">第一步</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#第二步"><span class="nav-number">3.0.2.</span> <span class="nav-text">第二步</span></a></li></ol></li></ol><li class="nav-item nav-level-2"><a class="nav-link" href="#目录创建"><span class="nav-number">4.</span> <span class="nav-text">目录创建</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#打开相机"><span class="nav-number">5.</span> <span class="nav-text">打开相机</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#打开相册"><span class="nav-number">6.</span> <span class="nav-text">打开相册</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#原生裁剪"><span class="nav-number">7.</span> <span class="nav-text">原生裁剪</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#相机相册以及裁剪回调"><span class="nav-number">8.</span> <span class="nav-text">相机相册以及裁剪回调</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#总结"><span class="nav-number">9.</span> <span class="nav-text">总结</span></a></li></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">土逗牛肉</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
	var str = GetUrlParam('height');
    if (str === '0'){
		document.getElementById('header').style.display = 'none';
		document.getElementById('comments').style.display = 'none';
		document.getElementById('footer').style.display = 'none';
		document.getElementsByClassName('post-footer').item(0).style.display = 'none'
	}
	
    function GetUrlParam(paraName) {
        var url = document.location.toString();
        var arrObj = url.split("?");

        if (arrObj.length > 1) {
            var arrPara = arrObj[1].split("&");
            var arr;

            for (var i = 0; i < arrPara.length; i++) {
                 arr = arrPara[i].split("=");

                if (arr != null && arr[0] === paraName) {
                    return arr[1];
                }
            }
            return "";
        } else {
            return "";
        }
    }
</script>









  












  
  
    <script type="text/javascript" src="/limh/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/limh/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/limh/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/limh/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/limh/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/limh/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/limh/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/limh/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/limh/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
